//    entity language compiler Copyright (C) 2011 Vincent Belliard

//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation version 3 of the License.

//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "types.h"

// }{--------------------------------------------------------------

unsigned strlen_utf8(const char *data)
{
unsigned length = 0 ;
for(;;)
	{
	unsigned loc = *(unsigned char *)data++ ;
	if (loc == 0) return length ;
	if (loc < 0x80)
		{
		length++ ;
		}
	else if (loc < 0xE0)
		{
		loc = *(unsigned char *)data ;
		if (loc == 0) return length ;
		if ((loc & 0xC0) == 0x80)
			{
			data++ ;
			length++ ;
			}
		}
	else if (loc < 0xF0)
		{
		loc = *(unsigned char *)data ;
		if (loc == 0) return length ;
		if ((loc & 0xC0) == 0x80)
			{
			data++ ;
			loc = *(unsigned char *)data ;
			if (loc == 0) return length ;
			if ((loc & 0xC0) == 0x80)
				{
				data++ ;
				length++ ;
				}
			}
		}
	else if (loc < 0xF8)
		{
		loc = *(unsigned char *)data ;
		if (loc == 0) return length ;
		if ((loc & 0xC0) == 0x80)
			{
			data++ ;
			loc = *(unsigned char *)data ;
			if (loc == 0) return length ;
			if ((loc & 0xC0) == 0x80)
				{
				data++ ;
				loc = *(unsigned char *)data ;
				if (loc == 0) return length ;
				if ((loc & 0xC0) == 0x80)
					{
					data++ ;
					length++ ;
					}
				}
			}
		}
	}
}

// }{--------------------------------------------------------------

T_string::T_string(const T_string_reference &start, const T_string_reference &end)
{
T_string_content *loc = start.get_content() ;
if (end.get_content() != loc)
	{
	content = new T_string_content(0, 0, 0, (char *)"") ;
	}
else
	{
	unsigned size = end.offset - start.offset ;
	char *buffer = new char[size + 1] ;
	memcpy(buffer, loc->data + start.offset, size) ;
	buffer[size] = 0 ;
	content = new T_string_content(size, size, end.utf8_offset - start.utf8_offset, buffer) ;
	}
}

T_string::T_string(const T_string &left, const T_string &right)
{
unsigned size = left.content->size + right.content->size ;
char *buffer = new char[size + 1] ;
memcpy(buffer, left.content->data, left.content->size) ;
memcpy(buffer + left.content->size, right.content->data, right.content->size) ;
buffer[size] = 0 ;
content = new T_string_content(size, size, strlen_utf8(buffer), buffer) ;
}

void T_string::set(const T_string_reference &start, const T_string_reference &end)
{
content->sub_link() ;
T_string_content *loc = start.get_content() ;
if (end.get_content() != loc)
	{
	content = new T_string_content(0, 0, 0, (char *)"") ;
	}
else
	{
	unsigned size = end.offset - start.offset ;
	char *buffer = new char[size + 1] ;
	memcpy(buffer, loc->data + start.offset, size) ;
	buffer[size] = 0 ;
	content = new T_string_content(size, size, end.utf8_offset - start.utf8_offset, buffer) ;
	}
}

// }{--------------------------------------------------------------

unsigned T_string_reference::current(void) const
{
const unsigned char *src = (unsigned char *)content->data + offset ;
unsigned loc = *(unsigned char *)src++ ;
if (loc < 0x80) return loc ;
//fprintf(stderr, "loc=%x\n", loc) ;
if (loc < 0xE0)
	{
	unsigned result = (loc & 0x1F) << 6 ;
	loc = *(unsigned char *)src ;
//fprintf(stderr, "=> %x + %x\n", result, loc & 0x3F) ;
	if ((loc & 0xC0) == 0x80) result |= loc & 0x3F ;
	return result ;
	}
if (loc < 0xF0)
	{
	unsigned result = (loc & 0xF) << 12 ;
	loc = *(unsigned char *)src++ ;
	if ((loc & 0xC0) == 0x80)
		{
		result |= (loc & 0x3F) << 6 ;
		loc = *(unsigned char *)src ;
		if ((loc & 0xC0) == 0x80) result |= loc & 0x3F ;
		}
	return result ;
	}
if (loc < 0xF8)
	{
	unsigned result = (loc & 0x7) << 18 ;
	loc = *(unsigned char *)src++ ;
	if ((loc & 0xC0) == 0x80)
		{
		result |= (loc & 0x3F) << 12 ;
		loc = *(unsigned char *)src++ ;
		if ((loc & 0xC0) == 0x80)
			{
			result |= (loc & 0x3F) << 6 ;
			loc = *(unsigned char *)src ;
			if ((loc & 0xC0) == 0x80) result |= loc & 0x3F ;
			}
		}
	return result ;
	}
return 0 ;
}

void T_string_reference::increment(void)
{
const unsigned char *src = (unsigned char *)content->data + offset ;
for(;;)
	{
	unsigned loc = *(unsigned char *)src++ ;
	if (loc == 0) return ;
	offset++ ;
	if (loc < 0x80)
		{
		utf8_offset++ ;
		return ;
		}
	if (loc < 0xE0)
		{
		utf8_offset++ ;
		loc = *(unsigned char *)src ;
		if ((loc & 0xC0) == 0x80) offset++ ;
		return ;
		}
	if (loc < 0xF0)
		{
		utf8_offset++ ;
		loc = *(unsigned char *)src++ ;
		if ((loc & 0xC0) == 0x80)
			{
			offset++ ;
			loc = *(unsigned char *)src ;
			if ((loc & 0xC0) == 0x80) offset++ ;
			}
		return ;
		}
	if (loc < 0xF8)
		{
		utf8_offset++ ;
		loc = *(unsigned char *)src++ ;
		if ((loc & 0xC0) == 0x80)
			{
			offset++ ;
			loc = *(unsigned char *)src++ ;
			if ((loc & 0xC0) == 0x80)
				{
				offset++ ;
				loc = *(unsigned char *)src ;
				if ((loc & 0xC0) == 0x80) offset++ ;
				}
			}
		return ;
		}
	}
}

// }{--------------------------------------------------------------
